package com.hmdp.service.impl;

import com.hmdp.dto.Result;
import com.hmdp.entity.Shop;
import com.hmdp.mapper.ShopMapper;
import com.hmdp.service.IShopService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 根据id查询商铺信息
     * 使用Redis缓存版
     */
    @Override
    public Result queryById(Long id) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {

        // 1.从redis中查询店铺数据
        Map<Object, Object> shopMap = stringRedisTemplate.opsForHash().entries("cache:shop:" + id);

        // 2. 缓存命中（无论是否有数据）
        if (!shopMap.isEmpty()) {
            // 如果是防穿透的空值（主动存入的标识）
            if (shopMap.containsKey("__NULL__")) { // 约定特殊字段标识空值
                return Result.fail("店铺不存在");
            }
            // 转换为Shop对象并返回
            Shop shop = new Shop();
            // 手动逐个赋值（根据实际字段类型转换）
            shop.setId(shopMap.get("id") != null ? Long.valueOf(shopMap.get("id").toString()) : null);
            shop.setName(shopMap.get("name") != null ? shopMap.get("name").toString() : null);
            shop.setTypeId(shopMap.get("typeId") != null ? Long.valueOf(shopMap.get("typeId").toString()) : null);
            shop.setImages(shopMap.get("images") != null ? shopMap.get("images").toString() : null);
            shop.setArea(shopMap.get("area") != null ? shopMap.get("area").toString() : null);
            shop.setAddress(shopMap.get("address") != null ? shopMap.get("address").toString() : null);
            shop.setX(shopMap.get("x") != null ? Double.valueOf(shopMap.get("x").toString()) : null);
            shop.setY(shopMap.get("y") != null ? Double.valueOf(shopMap.get("y").toString()) : null);
            shop.setAvgPrice(shopMap.get("avgPrice") != null ? Long.valueOf(shopMap.get("avgPrice").toString()) : null);
            shop.setSold(shopMap.get("sold") != null ? Integer.valueOf(shopMap.get("sold").toString()) : null);
            shop.setComments(shopMap.get("comments") != null ? Integer.valueOf(shopMap.get("comments").toString()) : null);
            shop.setScore(shopMap.get("score") != null ? Integer.valueOf(shopMap.get("score").toString()) : null);
            shop.setOpenHours(shopMap.get("openHours") != null ? shopMap.get("openHours").toString() : null);

            // 处理 LocalDateTime 类型字段（需要根据实际存储格式解析）
            if (shopMap.get("createTime") != null) {
                // 假设 shopMap 中的时间是字符串格式（如 "2021-12-22T10:00:00"）
                shop.setCreateTime(LocalDateTime.parse(shopMap.get("createTime").toString()));
            }
            if (shopMap.get("updateTime") != null) {
                shop.setUpdateTime(LocalDateTime.parse(shopMap.get("updateTime").toString()));
            }

            // 处理额外字段（distance）
//            shop.setDistance(shopMap.get("distance") != null ? Double.valueOf(shopMap.get("distance").toString()) : null);

            return Result.ok(shop);
        }

        // 3.缓存未命中就查询数据库
        Shop shopData = getById(id);

        // 4.数据库中也不存在就返回错误
        if (shopData == null) {
            // 防止缓存穿透 在redis中添加特殊字段__NULL__， 并设置较小的有效期
            Map<String, String> nullMap = new HashMap<>();
            nullMap.put("__NULL__", "1");
            stringRedisTemplate.opsForHash().putAll("cache:shop:" + id, nullMap);
            stringRedisTemplate.expire("cache:shop:" + id, 2L, TimeUnit.MINUTES);
            return Result.fail("店铺不存在");
        }

        // 5.数据库中存在就写入redis并返回
        // 将Shop对象转为Map
        Map<String, String> shopDataMap = BeanUtils.describe(shopData);
        stringRedisTemplate.opsForHash().putAll("cache:shop:" + id, shopDataMap);
        // 设置有效期
        stringRedisTemplate.expire("cache:shop:" + id, 30L, TimeUnit.MINUTES);
        return  Result.ok(shopData);
    }

    /**
     * 获取锁
     * 基于setnx实现
     */
    private boolean tryLock(String key) {
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(flag);
    }

    /**
     * 释放锁
     */
    private void unLock(String key) {
        stringRedisTemplate.delete(key);
    }

    /**
     * 更新商铺
     * 同时redis缓存版
     */
    @Transactional // 注意更新数据库和删除缓存必须同时完成或不完成，因此需要事务
    @Override
    public Result update(Shop shop) {
        if (shop == null || shop.getId() == null) {
            return Result.fail("店铺ID不能为空");
        }

        // 1.更新数据库
        updateById(shop);

        // 2.删除缓存
        stringRedisTemplate.delete("cache:shop:" + shop.getId());

        return Result.ok();
    }
}
